package com.ruoyi.paipan.common;

import com.ruoyi.paipan.utils.HolidayUtil;
import com.ruoyi.paipan.utils.LunarUtil;
import com.ruoyi.paipan.utils.SolarUtil;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class Solar {
    public static int J2000 = 2451545;
    private int year;
    private int month;
    private int day;
    private int hour;
    private int minute;
    private int second;

    public Solar(int year, int month, int day, int hour, int minute, int second) {
        this.year = year;
        this.month = month;
        this.day = day;
        this.hour = hour;
        this.minute = minute;
        this.second = second;
        if (1582 == year && 10 == month) {
            if (day > 4 && day < 15) {
                throw new RuntimeException(String.format("wrong solar year %d month %d day %d", year, month, day));
            }
        }
        if (month < 1 || month > 12) {
            throw new RuntimeException(String.format("wrong month %d", month));
        }
        if (day < 1 || day > 31) {
            throw new RuntimeException(String.format("wrong day %d", day));
        }
        if (hour < 0 || hour > 23) {
            throw new RuntimeException(String.format("wrong hour %d", hour));
        }
        if (minute < 0 || minute > 59) {
            throw new RuntimeException(String.format("wrong minute %d", minute));
        }
        if (second < 0 || second > 59) {
            throw new RuntimeException(String.format("wrong second %d", second));
        }
    }


    public static Solar fromDate(Date date)
    {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH)+1;
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);
        return new Solar(year, month, day, hour, minute, second);
    }

    public static Solar fromJulianDay(double julianDay)
    {
        int d = (int)(julianDay + 0.5);
        double f = julianDay + 0.5 - d;
        int c;
        if (d >= 2299161) {
            c = (int)((d - 1867216.25) / 36524.25);
            d += 1 + c - (c / 4);
        }
        d += 1524;
        int year = (int)((d - 122.1) / 365.25);
        d -= (int)(365.25 * year);
        int month = (int)(d / 30.601);
        d -= (int)(30.601 * month);
        int day = d;
        if (month > 13) {
            month -= 13;
            year -= 4715;
        } else {
            month -= 1;
            year -= 4716;
        }
        f *= 24;
        int hour = (int)f;
        f -= hour;
        f *= 60;
        int minute = (int)f;
        f -= minute;
        f *= 60;
        int second = (int) Math.round(f);
        if (second > 59) {
            second -= 60;
            minute++;
        }
        if (minute > 59) {
            minute -= 60;
            hour++;
        }
        if (hour > 23) {
            hour -= 24;
            day++;
        }
        return fromYmdHms(year, month, day, hour, minute, second);
    }

    public static ArrayList<Solar> fromBaZi(String yearGanZhi, String monthGanZhi, String dayGanZhi, String timeGanZhi)
    {
        return fromBaZiBySect(yearGanZhi, monthGanZhi, dayGanZhi, timeGanZhi, 2);
    }

    public static ArrayList<Solar> fromBaZiBySect(String yearGanZhi, String monthGanZhi, String dayGanZhi, String timeGanZhi, int sect)
    {
        return fromBaZiBySectAndBaseYear(yearGanZhi, monthGanZhi, dayGanZhi, timeGanZhi, sect, 1900);
    }

    public static ArrayList<Solar> fromBaZiBySectAndBaseYear(String yearGanZhi, String monthGanZhi, String dayGanZhi, String timeGanZhi, int sect, int baseYear)
    {
        sect = (1 == sect) ? 1 : 2;
        ArrayList<Solar> l = new ArrayList<>();
        ArrayList<Integer> years = new ArrayList<>();
        Solar today = fromDate(new Date());
        int offsetYear = (today.getYear() - 4) % 60 - LunarUtil.getJiaZiIndex(yearGanZhi);
        if (offsetYear < 0) {
            offsetYear += 60;
        }
        int startYear = today.getYear() - offsetYear - 1;
        int minYear = baseYear - 2;
        while (startYear >= minYear) {
            years.add(startYear);
            startYear -= 60;
        }
        ArrayList<Integer> hours = new ArrayList<>();
        String timeZhi = timeGanZhi.substring(timeGanZhi.length() / 2);
        for (int i = 1, j = LunarUtil.ZHI.size(); i < j; i++) {
            if(LunarUtil.ZHI.get(i).compareTo(timeZhi) == 0){
                hours.add((i - 1)*2);
            }
        }
        if("子".compareTo(timeZhi) == 0){
            hours.add(23);
        }
        for (int i = 0; i < hours.size(); i++) {
            for (int i1 = 0; i1 < years.size(); i1++) {
                int hour = hours.get(i);
                Integer y = years.get(i1);
                int maxYear = y+3;
                int year = y;
                int month = 11;
                if(year < baseYear){
                    year = baseYear;
                    month = 1;
                }
                Solar solar = fromYmdHms(year, month, 1, hour, 0, 0);
                while (solar.getYear() <= maxYear){
                    Lunar lunar = solar.getLunar();
                    String dgz = (2 == sect) ? lunar.getDayInGanZhiExact2() : lunar.getDayInGanZhiExact();
                    if(lunar.getYearInGanZhiExact().compareTo(yearGanZhi) == 0 && lunar.getMonthInGanZhiExact().compareTo(monthGanZhi) == 0 && dgz.compareTo(dayGanZhi) == 0 && lunar.getTimeInGanZhi().compareTo(timeGanZhi) == 0){
                        l.add(solar);
                        break;
                    }
                    solar = solar.next(1);
                }
            }
        }
        return l;
    }

    public static Solar fromYmd(int year, int month, int day)
    {
        return new Solar(year, month, day, 0, 0, 0);
    }

    public static Solar fromYmdHms(int year, int month, int day, int hour, int minute, int second)
    {
        return new Solar(year, month, day, hour, minute, second);
    }

    public String toYmd()
    {
        return String.format("%04d-%02d-%02d", year, month, day);
    }

    public String toYmdHms()
    {
        return toYmd() + " " + String.format("%02d:%02d:%02d", hour, minute, second);
    }

    public String toFullString()
    {
        String s = toYmdHms();
        if (isLeapYear()) {
            s += " 闰年";
        }
        s += " 星期" + getWeekInChinese();
        for (String festival : getFestivals()) {
            s += " (" + festival + ")";
        }
        s += " " + getXingZuo() + "座";
        return s;
    }

    public int getYear()
    {
        return year;
    }

    public int getMonth()
    {
        return month;
    }

    public int getDay()
    {
        return day;
    }

    public int getHour()
    {
        return hour;
    }

    public int getMinute()
    {
        return minute;
    }

    public int getSecond()
    {
        return second;
    }

    public Double getJulianDay()
    {
        int y = year;
        int m = month;
        double d = day + ((double)(second / 60 + minute) / 60 + hour) / 24;
        int n = 0;
        boolean g = false;
        if (y * 372 + m * 31 + d >= 588829) {
            g = true;
        }
        if (m <= 2) {
            m += 12;
            y--;
        }
        if (g) {
            n = (y / 100);
            n = 2 - n + (n / 4);
        }
        return (int)(365.25 * (y + 4716)) + (int)(30.6001 * (m + 1)) + d + n - 1524.5;
    }

    public Lunar getLunar()
    {
        return Lunar.fromSolar(this);
    }

    public String toString()
    {
        return toYmd();
    }


    public boolean isLeapYear()
    {
        return SolarUtil.isLeapYear(year);
    }

    public String getWeekInChinese()
    {
        return SolarUtil.WEEK.get(getWeek());
    }

    public String getXingZuo()
    {
        int index = 11;
        int y = month * 100 + day;
        if (y >= 321 && y <= 419) {
            index = 0;
        } else if (y >= 420 && y <= 520) {
            index = 1;
        } else if (y >= 521 && y <= 621) {
            index = 2;
        } else if (y >= 622 && y <= 722) {
            index = 3;
        } else if (y >= 723 && y <= 822) {
            index = 4;
        } else if (y >= 823 && y <= 922) {
            index = 5;
        } else if (y >= 923 && y <= 1023) {
            index = 6;
        } else if (y >= 1024 && y <= 1122) {
            index = 7;
        } else if (y >= 1123 && y <= 1221) {
            index = 8;
        } else if (y >= 1222 || y <= 119) {
            index = 9;
        } else if (y <= 218) {
            index = 10;
        }
        return SolarUtil.XING_ZUO.get(index);
    }

    public List<String> getFestivals()
    {
        List<String> l = new ArrayList<>();
        String key = month + "-" + day;
        if (SolarUtil.FESTIVAL.containsKey(key) && !SolarUtil.FESTIVAL.get(key).isEmpty()) {
            l.add(SolarUtil.FESTIVAL.get(key));
        }
        int weeks = (int) Math.ceil(day / 7.0);
        int week = getWeek();
        key = month + "-" + weeks + "-" + week;
        if (SolarUtil.WEEK_FESTIVAL.containsKey(key) && !SolarUtil.WEEK_FESTIVAL.get(key).isEmpty()) {
            l.add(SolarUtil.WEEK_FESTIVAL.get(key));
        }
        if (day + 7 > SolarUtil.getDaysOfMonth(year, month)) {
            key = month + "-0-" + week;
            if (SolarUtil.WEEK_FESTIVAL.containsKey(key) && !(SolarUtil.WEEK_FESTIVAL.get(key).isEmpty())) {
                l.add(SolarUtil.WEEK_FESTIVAL.get(key));
            }
        }
        return l;
    }

    public List<String> getOtherFestivals()
    {
        List<String>  l = new ArrayList<>();
        String key = month + "-" + day;
        if (!SolarUtil.OTHER_FESTIVAL.get(key).isEmpty()) {
            l.addAll(SolarUtil.OTHER_FESTIVAL.get(key));
        }
        return l;
    }

    public Integer subtract(Solar solar)
    {
        return SolarUtil.getDaysBetween(solar.getYear(), solar.getMonth(), solar.getDay(), getYear(), getMonth(), getDay());
    }

    public int subtractMinute(Solar solar)
    {
        int days = subtract(solar);
        int cm = getHour() * 60 + getMinute();
        int sm = solar.getHour() * 60 + solar.getMinute();
        int m = cm - sm;
        if (m < 0) {
            m += 1440;
            days--;
        }
        m += days * 1440;
        return m;
    }

    public boolean isAfter(Solar solar)
    {
        if (year > solar.getYear()) {
            return true;
        }
        if (year < solar.getYear()) {
            return false;
        }
        if (month > solar.getMonth()) {
            return true;
        }
        if (month < solar.getMonth()) {
            return false;
        }
        if (day > solar.getDay()) {
            return true;
        }
        if (day < solar.getDay()) {
            return false;
        }
        if (hour > solar.getHour()) {
            return true;
        }
        if (hour < solar.getHour()) {
            return false;
        }
        if (minute > solar.getMinute()) {
            return true;
        }
        if (minute < solar.getMinute()) {
            return false;
        }
        return second > solar.second;
    }

    public boolean isBefore(Solar solar)
    {
        if (year > solar.getYear()) {
            return false;
        }
        if (year < solar.getYear()) {
            return true;
        }
        if (month > solar.getMonth()) {
            return false;
        }
        if (month < solar.getMonth()) {
            return true;
        }
        if (day > solar.getDay()) {
            return false;
        }
        if (day < solar.getDay()) {
            return true;
        }
        if (hour > solar.getHour()) {
            return false;
        }
        if (hour < solar.getHour()) {
            return true;
        }
        if (minute > solar.getMinute()) {
            return false;
        }
        if (minute < solar.getMinute()) {
            return true;
        }
        return second < solar.second;
    }

    public Solar nextYear(int years)
    {
        int y = year + years;
        int m = month;
        int d = day;
        if (2 == m) {
            if (d > 28) {
                if (!SolarUtil.isLeapYear(y)) {
                    d = 28;
                }
            }
        }
        if (1582 == y && 10 == m) {
            if (d > 4 && d < 15) {
                d += 10;
            }
        }
        return fromYmdHms(y, m, d, hour, minute, second);
    }

    public Solar nextMonth(int months)
    {
        SolarMonth month = SolarMonth.fromYm(year, this.month).next(months);
        int y = month.getYear();
        int m = month.getMonth();
        int d = day;
        if (2 == m) {
            if (d > 28) {
                if (!SolarUtil.isLeapYear(y)) {
                    d = 28;
                }
            }
        }
        if (1582 == y && 10 == m) {
            if (d > 4 && d < 15) {
                d += 10;
            }
        }
        return fromYmdHms(y, m, d, hour, minute, second);
    }

    public Solar next(int days)
    {
        int y = year;
        int m = month;
        int d = day;
        if (1582 == y && 10 == m) {
            if (d > 4) {
                d -= 10;
            }
        }
        if (days > 0) {
            d += days;
            int daysInMonth = SolarUtil.getDaysOfMonth(y, m);
            while (d > daysInMonth) {
                d -= daysInMonth;
                m++;
                if (m > 12) {
                    m = 1;
                    y++;
                }
                daysInMonth = SolarUtil.getDaysOfMonth(y, m);
            }
        } else if (days < 0) {
            while (d + days <= 0) {
                m--;
                if (m < 1) {
                    m = 12;
                    y--;
                }
                d += SolarUtil.getDaysOfMonth(y, m);
            }
            d += days;
        }
        if (1582 == y && 10 == m) {
            if (d > 4) {
                d += 10;
            }
        }
        return fromYmdHms(y, m, d, hour, minute, second);
    }

    public Solar nextHour(int hours)
    {
        int h = hour + hours;
        int n = h < 0 ? -1 : 1;
        hour = (int)Math.abs(h);
        int days = (int)(hour / 24) * n;
        hour = (hour % 24) * n;
        if (hour < 0) {
            hour += 24;
            days--;
        }
        Solar solar = next(days);
        return fromYmdHms(solar.getYear(), solar.getMonth(), solar.getDay(), hour, solar.getMinute(), solar.getSecond());
    }

    public int getWeek()
    {
        Solar start = fromYmd(1582, 10, 15);
        int y = year;
        int m = month;
        int d = day;
        Solar current = fromYmd(y, m, d);
        if (m < 3) {
            m += 12;
            y--;
        }
        int c = (int)(y / 100);
        y = y - c * 100;
        int x = y + (int)(y / 4) + (int)(c / 4) - 2 * c;
        int w;
        if (current.isBefore(start)) {
            w = (x + (int)((13 * (m + 1)) / 5) + d + 2) % 7;
        } else {
            w = (x + (int)((26 * (m + 1)) / 10) + d - 1) % 7;
        }
        return (w + 7) % 7;
    }

    public Solar nextWorkday(int days)
    {
        Solar solar = fromYmdHms(year, month, day, hour, minute, second);
        if (days != 0) {
            int rest = Math.abs(days);
            int add = days < 0 ? -1 : 1;
            while (rest > 0) {
                solar = solar.next(add);
                boolean work = true;
                Holiday holiday = HolidayUtil.getHolidayByYmd(String.valueOf(solar.getYear()), solar.getMonth(), solar.getDay());
                if (null == holiday) {
                    int week = solar.getWeek();
                    if (0 == week || 6 == week) {
                        work = false;
                    }
                } else {
                    work = holiday.getWork();
                }
                if (work) {
                    rest -= 1;
                }
            }
        }
        return solar;
    }

    public int getSalaryRate()
    {
        if (month == 1 && day == 1) {
            return 3;
        }
        if (month == 5 && day == 1) {
            return 3;
        }
        if (month == 10 && day >= 1 && day <= 3) {
            return 3;
        }
        Lunar lunar = getLunar();
        if (lunar.getMonth() == 1 && lunar.getDay() >= 1 && lunar.getDay() <= 3) {
            return 3;
        }
        if (lunar.getMonth() == 5 && lunar.getDay() == 5) {
            return 3;
        }
        if (lunar.getMonth() == 8 && lunar.getDay() == 15) {
            return 3;
        }
        if ("清明".compareTo(lunar.getJieQi()) == 0) {
            return 3;
        }
        Holiday holiday = HolidayUtil.getHolidayByYmd(String.valueOf(year), month, day);
        if (null != holiday) {
            if (!holiday.getWork()) {
                return 2;
            }
        } else {
            int week = getWeek();
            if (week == 6 || week == 0) {
                return 2;
            }
        }
        return 1;
    }

    public static void main(String[] args) {
        System.out.println("hello world");
    }
}
